﻿using System;
using System.Collections.Generic;
using System.Reflection;
using MugenInjection.Bindings.Builders;
using MugenInjection.Core.Components;
using MugenInjection.Delegates;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;
using MugenInjection.Interface.Components;
using MugenInjection.Syntax;
using MugenInjection.Syntax.Type;

#if!NOEXPRESSION
using System.Linq.Expressions;
#endif

namespace MugenInjection
{
    /// <summary>
    /// Extension for <see cref="IInjector"/>.
    /// </summary>
    internal static class InjectorExtensionUtils
    {
        #region Public methods

        #region Get

        /// <summary>
        /// Gets an instance of the specified service.
        /// </summary>
        /// <param name="injector"></param>
        /// <param name="service"></param>
        /// <param name="member"> </param>
        /// <param name="parameterInfo"> </param>
        /// <param name="specialParameters"></param>
        /// <param name="parameters"></param> 
        /// <returns></returns>
        public static object Get(IInjectorResolver injector, Type service, MemberInfo member,
                                 ParameterInfo parameterInfo,
                                 IDictionary<string, object> specialParameters,
                                 IEnumerable<IInjectionParameter> parameters)
        {
            return
                injector.Resolve(injector.CreateContext(service, member, parameterInfo, parameters, specialParameters));
        }

#if !NOEXPRESSION

    /// <summary>
    /// Gets an instance of the specified service <see cref="T"/> use expression for add parameters (for into type).
    /// </summary>
    /// <typeparam name="T">The specified service type.</typeparam>
    /// <param name="injector">The specified <see cref="IInjector"/>.</param>
    /// <param name="member"> Type when injected specified service.</param>
    /// <param name="parameterInfo">The specified <see cref="ParameterInfo"/>. </param>
    /// <param name="expression">The specified <see cref="Expression"/>. </param>
    /// <param name="specialParameters"> </param>
    /// <param name="parameters">The specified parameters.</param>
    /// <returns></returns>
        public static T Get<T>(IInjectorResolver injector, MemberInfo member, ParameterInfo parameterInfo, Expression<Func<ResolveContext, T>> expression,
                               IDictionary<string, object> specialParameters, IEnumerable<IInjectionParameter> parameters)
        {
            List<IInjectionParameter> list = parameters == null
                                                 ? new List<IInjectionParameter>()
                                                 : new List<IInjectionParameter>(parameters);
            ConstructorInfo constructorInfo;
            list.AddRange(InjectorUtilsInternal.ParseNewExpression(expression, out constructorInfo));
            return (T)Get(injector, typeof(T), member, parameterInfo, specialParameters, list.ToArray());
        }

#endif

        #endregion

        #region Try get

        /// <summary>
        /// Try gets an instance of the specified service.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="result">Result object. </param>
        /// <param name="service">The specified service type.</param>
        /// <param name="member"> </param>
        /// <param name="parameterInfo"> </param>
        /// <param name="specialParameters">Special parameters for resolve current service. </param>
        /// <param name="parameters">The specified parameters.</param>
        /// <returns></returns>
        public static bool TryGet(IInjectorResolver injector, out object result, Type service, MemberInfo member,
                                  ParameterInfo parameterInfo,
                                  IDictionary<string, object> specialParameters,
                                  IEnumerable<IInjectionParameter> parameters)
        {
            result = null;
            IBindingContext bindingContext = injector.CreateContext(service, member, parameterInfo, parameters,
                                                                    specialParameters);
            bool canResolve = injector.CanResolve(bindingContext, true, true);
            if (canResolve)
            {
                try
                {
                    result = injector.Resolve(bindingContext);
                }
                catch
                {
                    canResolve = false;
                }
            }
            return canResolve;
        }

        /// <summary>
        /// Try gets an instance of the specified service <see cref="T"/>.
        /// </summary>
        /// <typeparam name="T">The specified service type.</typeparam>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="result">Result object. </param>
        /// <param name="member"> </param>
        /// <param name="parameterInfo"> </param>
        /// <param name="specialParameters"> Special parameters for resolve current service.</param>
        /// <param name="parameters">The specified parameters.</param>
        /// <returns></returns>
        public static bool TryGet<T>(IInjectorResolver injector, out T result, MemberInfo member,
                                     ParameterInfo parameterInfo,
                                     IDictionary<string, object> specialParameters,
                                     IEnumerable<IInjectionParameter> parameters)
        {
            object obj;
            bool canResolve =
                TryGet(injector, out obj, typeof(T), member, parameterInfo, specialParameters, parameters) && obj is T;
            if (canResolve)
                result = (T)obj;
            else
                result = default(T);
            return canResolve;
        }

#if !NOEXPRESSION

    /// <summary>
    /// Try gets an instance of the specified service <see cref="T"/> use expression for add parameters (for into type).
    /// </summary>
    /// <typeparam name="T">The specified service type.</typeparam>
    /// <param name="injector">The specified <see cref="IInjector"/>.</param>
    /// <param name="result">Result object. </param>
    /// <param name="member"> Type when injected specified service.</param>
    /// <param name="parameterInfo"> </param>
    /// <param name="expression">The specified <see cref="Expression"/>. </param>
    /// <param name="specialParameters">Special parameters for resolve current service. </param>
    /// <param name="parameters">The specified parameters.</param>
    /// <returns></returns>
        public static bool TryGet<T>(IInjectorResolver injector, out T result, MemberInfo member, ParameterInfo parameterInfo,
                                         Expression<Func<ResolveContext, T>> expression,
                                         IDictionary<string, object> specialParameters,
                                         IEnumerable<IInjectionParameter> parameters)
        {
            var newParams = parameters == null
                                ? new List<IInjectionParameter>()
                                : new List<IInjectionParameter>(parameters);
            ConstructorInfo constructorInfo;
            newParams.AddRange(InjectorUtilsInternal.ParseNewExpression(expression, out constructorInfo));
            return TryGet(injector, out result, member, parameterInfo, specialParameters, newParams.ToArray());
        }

#endif

        /// <summary>
        /// Try gets all available instances of the specified service in the specified <see cref="IInjector"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="result">Result objects. </param>
        /// <param name="service">The specified service type.</param>
        /// <param name="member"> Type where injected specified service.</param>
        /// <param name="parameterInfo"> </param>
        /// <param name="specialParameters">Special parameters for resolve current service. </param>
        /// <param name="all"> </param>
        /// <param name="parameters">The specified parameters. </param>
        /// <returns></returns>
        public static bool TryGetAllInCurrent(IInjectorResolver injector, out IEnumerable<object> result,
                                              Type service,
                                              MemberInfo member, ParameterInfo parameterInfo,
                                              IDictionary<string, object> specialParameters, bool all,
                                              IInjectionParameter[] parameters)
        {
            result = null;
            bool canResolve = CanResolve(injector, service, member, parameterInfo, parameters, specialParameters, false,
                                         true);
            if (canResolve)
            {
                try
                {
                    result = GetAllInCurrent(injector, service, member, parameterInfo, specialParameters, all,
                                             parameters);
                }
                catch
                {
                    result = null;
                }
            }
            return result != null;
        }


        /// <summary>
        /// Try gets all available instances of the specified service in the specified <see cref="IInjector"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="result">Result objects. </param>
        /// <param name="member"> Type where injected specified service.</param>
        /// <param name="parameterInfo"> </param>
        /// <param name="specialParameters">Special parameters for resolve current service. </param>
        /// <param name="all"> </param>
        /// <param name="parameters">The specified parameters. </param>
        /// <returns></returns>
        public static bool TryGetAllInCurrent<T>(IInjectorResolver injector, out IEnumerable<T> result,
                                                 MemberInfo member, ParameterInfo parameterInfo,
                                                 IDictionary<string, object> specialParameters, bool all,
                                                 IInjectionParameter[] parameters)
        {
            result = null;
            bool canResolve = CanResolve(injector, typeof(T), member, parameterInfo, parameters, specialParameters,
                                         false, true);
            if (canResolve)
            {
                try
                {
                    result =
                        InjectorUtilsInternal.Cast<T>(GetAllInCurrent(injector, typeof(T), member, parameterInfo,
                                                                      specialParameters, all, parameters));
                }
                catch
                {
                    result = null;
                }
            }
            return result != null;
        }


        /// <summary>
        /// Try gets all available instances of the specified service in the specified <see cref="IInjector"/> include all of its child and parent <see cref="IInjector"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="result">Result objects. </param>
        /// <param name="service"> </param>
        /// <param name="member">Type where injected specified service. </param>
        /// <param name="parameter"> </param>
        /// <param name="specialParameters">Special parameters for resolve current service. </param>
        /// <param name="all"> </param>
        /// <param name="parameters">The specified parameters. </param>
        /// <returns></returns>
        public static bool TryGetAll(IInjectorResolver injector, out IEnumerable<object> result, Type service, MemberInfo member,
                                     ParameterInfo parameter,
                                     IDictionary<string, object> specialParameters, bool all,
                                     IInjectionParameter[] parameters)
        {
            result = null;
            bool canResolve = CanResolve(injector, service, member, parameter, parameters, specialParameters, true, true);
            if (canResolve)
            {
                try
                {
                    result = GetAll(injector, service, member, parameter, specialParameters, all, parameters);
                }
                catch
                {
                    result = null;
                }
            }
            return result != null;
        }

        /// <summary>
        /// Try gets all available instances of the specified service in the specified <see cref="IInjector"/> include all of its child and parent <see cref="IInjector"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="result">Result objects. </param>
        /// <param name="member">Type where injected specified service. </param>
        /// <param name="parameter"> </param>
        /// <param name="specialParameters">Special parameters for resolve current service. </param>
        /// <param name="all"> </param>
        /// <param name="parameters">The specified parameters. </param>
        /// <returns></returns>
        public static bool TryGetAll<T>(IInjectorResolver injector, out IEnumerable<T> result, MemberInfo member,
                                        ParameterInfo parameter,
                                        IDictionary<string, object> specialParameters, bool all,
                                        IInjectionParameter[] parameters)
        {
            result = null;
            bool canResolve = CanResolve(injector, typeof(T), member, parameter, parameters, specialParameters, true,
                                         true);
            if (canResolve)
            {
                try
                {
                    result =
                        InjectorUtilsInternal.Cast<T>(GetAll(injector, typeof(T), member, parameter, specialParameters,
                                                             all, parameters));
                }
                catch
                {
                    result = null;
                }
            }
            return result != null;
        }

        #endregion

        #region Get all

        /// <summary>
        /// Gets all available instances of the specified service in the specified <see cref="IInjector"/> (for into type).
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="service">The specified service type.</param>
        /// <param name="member">Type when injected specified service.</param>
        /// <param name="parameterInfo"> </param>
        /// <param name="specialParameters"> </param>
        /// <param name="all"> </param>
        /// <param name="parameters"> </param>
        /// <returns></returns>
        public static IEnumerable<object> GetAllInCurrent(IInjectorResolver injector, Type service, MemberInfo member,
                                                          ParameterInfo parameterInfo,
                                                          IDictionary<string, object> specialParameters, bool all,
                                                          IEnumerable<IInjectionParameter> parameters)
        {
            if (all)
                return injector.ResolveAll(service, parameters, specialParameters, false);
            return
                injector.ResolveAll(injector.CreateContext(service, member, parameterInfo, parameters, specialParameters), false);
        }

        /// <summary>
        /// Gets all available instances of the specified service in the specified <see cref="IInjector"/> include all of its child and parent <see cref="IInjector"/> (for into type).
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="service">The specified service type.</param>
        /// <param name="member">Type when injected specified service.</param>
        /// <param name="parameter"> </param>
        /// <param name="specialParameters"> </param>
        /// <param name="all"> </param>
        /// <param name="parameters"> </param>
        /// <returns></returns>
        public static IEnumerable<object> GetAll(IInjectorResolver injector, Type service, MemberInfo member,
                                                 ParameterInfo parameter,
                                                 IDictionary<string, object> specialParameters, bool all,
                                                 IInjectionParameter[] parameters)
        {

            if (all)
                return injector.ResolveAll(service, parameters, specialParameters, true);

            IBindingContext context = injector.CreateContext(service, member, parameter, parameters, specialParameters);
            return injector.ResolveAll(context, true);
        }

        #endregion

        #region Bind

        /// <summary>
        /// Declares a binding for the specified service.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="withManualBuild">Add with manual build.</param>
        /// <param name="service">The specified service type.</param>
        /// <returns></returns>
        public static IBindingSyntax Bind(IInjector injector, bool withManualBuild, Type[] service)
        {
            var builder = new BindingBuilder<object, object, object, object>(service, injector, withManualBuild);
            if (!withManualBuild)
                injector.Bindings.AddBindingBuilder(builder);
            return builder;
        }

        /// <summary>
        /// Declares a binding for the specified service <see cref="T"/>.
        /// </summary>
        /// <typeparam name="T">The specified service type.</typeparam>
        /// <param name="withManualBuild">Add with manual build.</param>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <returns></returns>
        public static IGenericBindingSyntax<T> Bind<T>(IInjector injector, bool withManualBuild)
        {
            var builder = new BindingBuilder<T, object, object, object>(new[] { typeof(T) }, injector, withManualBuild);
            if (!withManualBuild)
                injector.Bindings.AddBindingBuilder(builder);
            return builder;
        }

        /// <summary>
        /// Declares a binding for the specified services
        /// </summary>
        /// <typeparam name="T1">The specified service type.</typeparam>
        /// <typeparam name="T2">The specified service type.</typeparam>
        /// <param name="withManualBuild">Add with manual build.</param>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <returns></returns>
        public static IGenericBindingSyntax<T1, T2> Bind<T1, T2>(IInjector injector, bool withManualBuild)
        {
            var builder = new BindingBuilder<T1, T2, object, object>(new[] { typeof(T1), typeof(T2) }, injector,
                                                                     withManualBuild);
            if (!withManualBuild)
                injector.Bindings.AddBindingBuilder(builder);
            return builder;
        }

        /// <summary>
        /// Declares a binding for the specified services
        /// </summary>
        /// <typeparam name="T1">The specified service type.</typeparam>
        /// <typeparam name="T2">The specified service type.</typeparam>
        /// <typeparam name="T3">The specified service type.</typeparam>
        /// <param name="withManualBuild">Add with manual build.</param>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <returns></returns>
        public static IGenericBindingSyntax<T1, T2, T3> Bind<T1, T2, T3>(IInjector injector, bool withManualBuild)
        {
            var builder = new BindingBuilder<T1, T2, T3, object>(new[] { typeof(T1), typeof(T2), typeof(T3) }, injector,
                                                                 withManualBuild);
            if (!withManualBuild)
                injector.Bindings.AddBindingBuilder(builder);
            return builder;
        }

        /// <summary>
        /// Declares a binding for the specified services
        /// </summary>
        /// <typeparam name="T1">The specified service type.</typeparam>
        /// <typeparam name="T2">The specified service type.</typeparam>
        /// <typeparam name="T3">The specified service type.</typeparam>
        /// <typeparam name="T4">The specified service type.</typeparam>
        /// <param name="withManualBuild">Add with manual build.</param>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <returns></returns>
        public static IGenericBindingSyntax<T1, T2, T3, T4> Bind<T1, T2, T3, T4>(IInjector injector,
                                                                                 bool withManualBuild)
        {
            var builder = new BindingBuilder<T1, T2, T3, T4>(
                new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, injector, withManualBuild);
            if (!withManualBuild)
                injector.Bindings.AddBindingBuilder(builder);
            return builder;
        }

        /// <summary>
        /// Declares a binding for the specified service <see cref="T"/>.
        /// </summary>
        /// <typeparam name="T">The specified service type.</typeparam>
        /// <typeparam name="TTypeTo">Type to bind specified service. </typeparam>
        /// <param name="withManualBuild">Add with manual build.</param>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <returns></returns>
        public static ITypeCallbackConstructorObjectPriorityScopeUseWhenWithSyntax<TTypeTo> BindTo<T, TTypeTo>(
            IInjector injector, bool withManualBuild) where TTypeTo : T
        {
            return Bind<T>(injector, withManualBuild).To<TTypeTo>();
        }

        #endregion

        #region Rebind

        /// <summary>
        /// Removes any existing bindings for the specified services, and declares a new one.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="type">The specified service types.</param>
        /// <returns></returns>
        public static IBindingSyntax Rebind(IInjector injector, Type type)
        {
            Unbind(injector, type);
            return Bind(injector, false, new[] { type });
        }

        /// <summary>
        /// Removes any existing bindings for the specified services <see cref="T"/>, and declares a new one.
        /// </summary>
        /// <typeparam name="T">The specified service type.</typeparam>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <returns></returns>
        public static IGenericBindingSyntax<T> Rebind<T>(IInjector injector)
        {
            Unbind(injector, typeof(T));
            return Bind<T>(injector, false);
        }

        /// <summary>
        /// Removes any existing bindings with specified condition for the specified services, and declares a new one.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="type">The specified service type.</param>
        /// <param name="member">Type when injected specified service.</param>
        /// <param name="parameter"> </param>
        /// <param name="specialParameters"> </param>
        /// <param name="parameters">The specified binding parameters. </param>
        public static IBindingSyntax Rebind(IInjector injector, Type type, MemberInfo member, ParameterInfo parameter,
                                            IDictionary<string, object> specialParameters,
                                            params IInjectionParameter[] parameters)
        {
            Unbind(injector, type, member, parameter, specialParameters, parameters);
            return Bind(injector, false, new[] { type });
        }

        /// <summary>
        /// Removes any existing bindings with specified condition for the specified services <see cref="T"/>, and declares a new one.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="member">Type when injected specified service.</param>
        /// <param name="parameter"> </param>
        /// <param name="specialParameters"> </param>
        /// <param name="parameters">The specified binding parameters. </param>
        public static IGenericBindingSyntax<T> Rebind<T>(IInjector injector, MemberInfo member, ParameterInfo parameter,
                                                         IDictionary<string, object> specialParameters,
                                                         params IInjectionParameter[] parameters)
        {
            Unbind(injector, typeof(T), member, parameter, specialParameters, parameters);
            return Bind<T>(injector, false);
        }

        #endregion

        #region Unbind

        /// <summary>
        /// Unregisters all bindings for the specified service.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="type">The specified service type.</param>
        public static void Unbind(IInjectorBinder injector, Type type)
        {
            var whereServiceEqual = new List<IBinding>(injector.Bindings.WhereServiceEqual(type));
            for (int index = 0; index < whereServiceEqual.Count; index++)
            {
                IBinding binding = whereServiceEqual[index];
                injector.RemoveBinding(binding);
            }
        }

        /// <summary>
        /// Unregisters all bindings for the specified service, with specific condition.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="type">The specified service type.</param>
        /// <param name="member">Type when injected specified service.</param>
        /// <param name="parameter"> </param>
        /// <param name="specialParameters"> </param>
        /// <param name="parameters">The specified binding parameters. </param>
        public static void Unbind(IInjectorBinder injector, Type type, MemberInfo member, ParameterInfo parameter,
                                  IDictionary<string, object> specialParameters, IList<IInjectionParameter> parameters)
        {
            if (parameters == null)
                parameters = InjectorUtilsInternal.EmptyInjectionParams;
            IBindingContext bindingContext = injector.CreateBinderContext(type, member, parameter, parameters,
                                                                          specialParameters);
            var whereServiceEqual = new List<IBinding>(injector.Bindings.WhereCanResolve(bindingContext));
            for (int index = 0; index < whereServiceEqual.Count; index++)
            {
                IBinding binding = whereServiceEqual[index];
                injector.RemoveBinding(binding);
            }
        }

        #endregion

        #region Can resolve

        /// <summary>
        /// Check can resolve specified service.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>. </param>
        /// <param name="service">Service for resolve.</param>
        /// <param name="member"><see cref="MemberInfo"/> when injected specified service. Can be null.</param>
        /// <param name="parameterInfo"><see cref="ParameterInfo"/> when injected specified service. Can be null. </param>
        /// <param name="parameters">Parameters for service. </param>
        /// <param name="specialParameters">Special parameters for resolve specified service. </param>
        /// <param name="searchInParent">If set to <c>true</c> search in parent bindings.</param>
        /// <param name="includeImplicitBindings">If set to <c>false</c> implicit bindings are ignored.</param>
        /// <returns>Indicate can resolve.</returns>
        public static bool CanResolve(IInjectorResolver injector, Type service, MemberInfo member,
                                      ParameterInfo parameterInfo, IEnumerable<IInjectionParameter> parameters,
                                      IDictionary<string, object> specialParameters, bool searchInParent,
                                      bool includeImplicitBindings)
        {
            if (parameters == null)
                parameters = InjectorUtilsInternal.EmptyInjectionParams;
            return
                injector.CanResolve(
                    injector.CreateContext(service, member, parameterInfo, parameters, specialParameters), searchInParent,
                    includeImplicitBindings);
        }

        #endregion

        #region Release

        /// <summary>
        /// Release specified service in scope.
        /// </summary>
        /// <param name="injector"> </param>
        /// <param name="service">Service for release.</param>
        /// <param name="member">Type when injected specified service.</param>
        /// <param name="parameters">The specified binding parameters. </param>
        /// <param name="parameterInfo"> </param>
        /// <param name="specialParameters">Special parameters for release specified service.</param>
        /// <param name="needDispose">Try dispose object.</param>
        /// <returns></returns>
        public static bool Release(IInjector injector, Type service, MemberInfo member, ParameterInfo parameterInfo,
                                   IDictionary<string, object> specialParameters,
                                   bool needDispose, IEnumerable<IInjectionParameter> parameters)
        {
            if (parameters == null)
                parameters = InjectorUtilsInternal.EmptyInjectionParams;
            return
                injector.Release(injector.CreateContext(service, member, parameterInfo, parameters, specialParameters),
                                 needDispose);
        }

        #endregion

        #region Scan modules

        /// <summary>
        /// Loads modules defined in the specified assemblies.
        /// </summary>
        /// <param name="injector"></param>
        /// <param name="assemblies">The assemblies to search.</param>
        public static void LoadModules(IInjector injector, params Assembly[] assemblies)
        {
            Validate.ArgumentNotNull(injector, "injector");
            Validate.ArgumentNotNull(assemblies, "assemblies");
            IModuleManagerComponent component;
            if (!injector.Components.TryGet(out component))
            {
                component = new ModuleManagerComponent();
                injector.Components.Add(component);
            }
            component.LoadModules(assemblies);
        }

        #endregion

        #region Decorators

        /// <summary>
        /// Decorate all components implementing service <typeparamref name="TService"/> using the provided <paramref name="decorator"/> function.
        /// </summary>
        /// <typeparam name="TService">Service type being decorated.</typeparam>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="decorator">Function decorating a component instance.</param>
        public static IDynamicConverter BindAsDecoratorForAll<TService>(IInjector injector, Func<IBindingContext, TService, TService> decorator)
        {
            Validate.ArgumentNotNull(injector, "injector");
            Validate.ArgumentNotNull(decorator, "decorator");
            var converter = new DecoratorDynamicConverter(typeof(TService), (context, o) => decorator(context, (TService)o), context => context.Service == typeof(TService));
            var activatorComponent = injector.Components.BindingActivatorComponent;
            var converterProvider = activatorComponent.GetConverter<DynamicConverterProvider>();
            if (converterProvider == null)
            {
                converterProvider = new DynamicConverterProvider();
                activatorComponent.AddConverter(converterProvider);
            }
            converterProvider.AddConverter(converter);
            return converter;
        }

        /// <summary>
        /// Decorate components with specified condition implementing service <typeparamref name="TService"/> using the provided <paramref name="decorator"/> function.
        /// </summary>
        /// <typeparam name="TService">Service type being decorated.</typeparam>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="buildCondition">The specified delegate to build condition.</param>
        /// <param name="decorator">Function decorating a component instance.</param>
        public static IDynamicConverter BindAsDecoratorFor<TService>(IInjector injector, Action<IConditionBuilder<TService>> buildCondition, Func<IBindingContext, TService, TService> decorator)
        {
            Validate.ArgumentNotNull(injector, "injector");
            Validate.ArgumentNotNull(buildCondition, "buildCondition");
            Validate.ArgumentNotNull(decorator, "decorator");
            var builder = new ConditionBuilder<TService>();
            buildCondition(builder);
            var @delegate = builder.Build();

            var converter = new DecoratorDynamicConverter(typeof(TService), (context, o) => decorator(context, (TService)o), @delegate);
            var activatorComponent = injector.Components.BindingActivatorComponent;
            var converterProvider = activatorComponent.GetConverter<DynamicConverterProvider>();
            if (converterProvider == null)
            {
                converterProvider = new DynamicConverterProvider();
                activatorComponent.AddConverter(converterProvider);
            }
            converterProvider.AddConverter(converter);
            return converter;
        }

        /// <summary>
        /// Unregisters all decorators for the specified service.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        public static bool UnbindAllDecoratorsFor<TService>(IInjector injector)
        {
            Validate.ArgumentNotNull(injector, "injector");
            var activatorComponent = injector.Components.BindingActivatorComponent;
            var converterProvider = activatorComponent.GetConverter<DynamicConverterProvider>();
            if (converterProvider == null)
                return false;
            var converters =
                converterProvider.GetAll<DecoratorDynamicConverter>(
                    converter => converter.ServiceToDecorate == typeof(TService));
            if (converters.Count == 0)
                return false;
            foreach (var converter in converters)
            {
                converterProvider.RemoveConverter(converter);
            }
            return true;
        }

        /// <summary>
        /// Unregisters decorators for the specified service, with specific condition.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="member">Type when injected specified service.</param>
        /// <param name="parameter"> </param>
        /// <param name="specialParameters"> </param>
        /// <param name="parameters">The specified binding parameters. </param>
        public static bool UnbindDecoratorsFor<TService>(IInjector injector, MemberInfo member, ParameterInfo parameter,
                                                         IDictionary<string, object> specialParameters, IList<IInjectionParameter> parameters)
        {
            Validate.ArgumentNotNull(injector, "injector");
            if (parameters == null)
                parameters = InjectorUtilsInternal.EmptyInjectionParams;
            if (specialParameters == null)
                specialParameters = InjectorUtilsInternal.ReadOnlyEmptyDictionary;

            var activatorComponent = injector.Components.BindingActivatorComponent;
            var converterProvider = activatorComponent.GetConverter<DynamicConverterProvider>();
            if (converterProvider == null)
                return false;

            Type type;
            var context = injector.CreateBinderContext(typeof (TService), member, parameter, parameters, specialParameters);
            var converters = converterProvider.GetAll<DecoratorDynamicConverter>(
                converter =>
                converter.ServiceToDecorate == typeof (TService) &&
                converter.CanResolve(context, out type));

            if (converters.Count == 0)
                return false;
            foreach (var converter in converters)
            {
                converterProvider.RemoveConverter(converter);
            }
            return true;
        }

        #endregion

        #region Adapters

        /// <summary>
        /// Adapts all components implementing service <typeparamref name="TFrom"/> to provide <typeparamref name="TTo"/> using the provided <paramref name="adapter"/> function.
        /// </summary>
        /// <typeparam name="TFrom">Service type to adapt from.</typeparam>
        /// <typeparam name="TTo">Service type to adapt to. Must not be the same as <typeparamref name="TFrom"/>.</typeparam>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="adapter">Function adapting <typeparamref name="TFrom"/> to service <typeparamref name="TTo"/>.</param>
        public static IDynamicConverter BindAsAdapterForAll<TFrom, TTo>(IInjector injector, Func<IBindingContext, TFrom, TTo> adapter)
        {
            Validate.ArgumentNotNull(injector, "injector");
            Validate.ArgumentNotNull(adapter, "adapter");

            var converter = new AdapterDynamicConverter(typeof(TFrom), typeof(TTo),
                                                        (context, o) => adapter(context, (TFrom)o),
                                                        context => context.Service == typeof(TTo));
            var activatorComponent = injector.Components.BindingActivatorComponent;
            var converterProvider = activatorComponent.GetConverter<DynamicConverterProvider>();
            if (converterProvider == null)
            {
                converterProvider = new DynamicConverterProvider();
                activatorComponent.AddConverter(converterProvider);
            }
            converterProvider.AddConverter(converter);
            return converter;
        }

        /// <summary>
        /// Adapts components with specified condition implementing service <typeparamref name="TFrom"/> to provide <typeparamref name="TTo"/> using the provided <paramref name="adapter"/> function.
        /// </summary>
        /// <typeparam name="TFrom">Service type to adapt from.</typeparam>
        /// <typeparam name="TTo">Service type to adapt to. Must not be the same as <typeparamref name="TFrom"/>.</typeparam>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="buildCondition">The specified delegate to build condition.</param>
        /// <param name="adapter">Function adapting <typeparamref name="TFrom"/> to service <typeparamref name="TTo"/>.</param>
        public static IDynamicConverter BindAsAdapterFor<TFrom, TTo>(IInjector injector, Action<IConditionBuilder<TTo>> buildCondition, Func<IBindingContext, TFrom, TTo> adapter)
        {
            Validate.ArgumentNotNull(injector, "injector");
            Validate.ArgumentNotNull(buildCondition, "buildCondition");
            Validate.ArgumentNotNull(adapter, "adapter");

            var builder = new ConditionBuilder<TTo>();
            buildCondition(builder);
            var @delegate = builder.Build();

            var converter = new AdapterDynamicConverter(typeof(TFrom), typeof(TTo),
                                                        (context, o) => adapter(context, (TFrom)o), @delegate);
            var activatorComponent = injector.Components.BindingActivatorComponent;
            var converterProvider = activatorComponent.GetConverter<DynamicConverterProvider>();
            if (converterProvider == null)
            {
                converterProvider = new DynamicConverterProvider();
                activatorComponent.AddConverter(converterProvider);
            }
            converterProvider.AddConverter(converter);
            return converter;
        }


        /// <summary>
        /// Unregisters all adapters for the specified service.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        public static bool UnbindAllAdaptersFor<TFrom, TTo>(IInjector injector)
        {
            Validate.ArgumentNotNull(injector, "injector");
            var activatorComponent = injector.Components.BindingActivatorComponent;
            var converterProvider = activatorComponent.GetConverter<DynamicConverterProvider>();
            if (converterProvider == null)
                return false;
            var converters =
                converterProvider.GetAll<AdapterDynamicConverter>(
                    converter => converter.TypeFrom == typeof(TFrom) && converter.TypeTo == typeof(TTo));
            if (converters.Count == 0)
                return false;
            foreach (var converter in converters)
            {
                converterProvider.RemoveConverter(converter);
            }
            return true;
        }

        /// <summary>
        /// Unregisters adapters for the specified service, with specific condition.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        /// <param name="member">Type when injected specified service.</param>
        /// <param name="parameter"> </param>
        /// <param name="specialParameters">The special parameters.</param>
        /// <param name="parameters">The specified binding parameters. </param>
        public static bool UnbindAdaptersFor<TFrom, TTo>(IInjector injector, MemberInfo member, ParameterInfo parameter,
                                                         IDictionary<string, object> specialParameters, IList<IInjectionParameter> parameters)
        {
            Validate.ArgumentNotNull(injector, "injector");
            if (parameters == null)
                parameters = InjectorUtilsInternal.EmptyInjectionParams;
            if (specialParameters == null)
                specialParameters = InjectorUtilsInternal.ReadOnlyEmptyDictionary;

            var activatorComponent = injector.Components.BindingActivatorComponent;
            var converterProvider = activatorComponent.GetConverter<DynamicConverterProvider>();
            if (converterProvider == null)
                return false;
            Type type;
            var context = injector.CreateBinderContext(typeof (TTo), member, parameter, parameters, specialParameters);
            var converters =
                converterProvider.GetAll<AdapterDynamicConverter>(
                    converter =>
                    converter.TypeFrom == typeof (TFrom) && converter.TypeTo == typeof (TTo) &&
                    converter.CanResolve(context, out type));
            if (converters.Count == 0)
                return false;
            foreach (var converter in converters)
            {
                converterProvider.RemoveConverter(converter);
            }
            return true;
        }

        #endregion

        #endregion
    }
}